unit InvoiceForm;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, DBModule, Vcl.StdCtrls, Vcl.Mask, IB,
  Vcl.ExtCtrls, Vcl.Grids, Vcl.DBGrids, Vcl.ComCtrls, IBSQL, RTFFrame, Data.DB,
  IBCustomDataSet, IBQuery, UserUtils, Vcl.DBCtrls, System.DateUtils;

type
  TfmInvoice = class(TForm)
    Label1: TLabel;
    lbDate: TLabel;
    Label2: TLabel;
    Label5: TLabel;
    Label6: TLabel;
    Label7: TLabel;
    btnSave: TButton;
    btnClose: TButton;
    sgDetails: TStringGrid;
    lblInvoiceNo: TLabel;
    Label3: TLabel;
    lbTotalAmount: TLabel;
    frmRTFEditor1: TfrmRTFEditor;
    frmRTFEditor2: TfrmRTFEditor;
    Label8: TLabel;
    Label9: TLabel;
    txtAmount: TEdit;
    btnAddDetails: TButton;
    btnDeleteDescription: TButton;
    cbFOCDI: TCheckBox;
    dtDateOfShoot: TDateTimePicker;
    Label4: TLabel;
    Label10: TLabel;
    dtDueDate: TDateTimePicker;
    Label11: TLabel;
    dbcbProduct: TDBLookupComboBox;
    dbcbCompanyName: TDBLookupComboBox;
    Label12: TLabel;
    Label13: TLabel;
    Label14: TLabel;
    Label15: TLabel;
    txtPublishOn: TEdit;
    txtPersonInCharge: TEdit;
    dbcbPhotographer1: TDBLookupComboBox;
    dbcbPhotographer2: TDBLookupComboBox;
    Label16: TLabel;
    txtJobSheetNo: TEdit;
    Label17: TLabel;
    procedure FormCreate(Sender: TObject);
    procedure btnCloseClick(Sender: TObject);
    procedure txtAmountKeyPress(Sender: TObject; var Key: Char);
    procedure txtAmountExit(Sender: TObject);
    procedure btnAddDetailsClick(Sender: TObject);
    procedure btnDeleteDescriptionClick(Sender: TObject);
    procedure sgDetailsDblClick(Sender: TObject);
    procedure btnSaveClick(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure frmRTFEditor1EditorChange(Sender: TObject);
    procedure cbCompanyNameChange(Sender: TObject);
    procedure frmRTFEditor2EditorChange(Sender: TObject);
    procedure txtAmountChange(Sender: TObject);
    procedure cbFOCDIClick(Sender: TObject);
    procedure dbcbCompanyNameCloseUp(Sender: TObject);
    procedure dtDateOfShootExit(Sender: TObject);
  private
    { Private declarations }
    Operation: TOperation;
    LastInvoiceNo: integer;
    procedure InitHeader;
    procedure AddNewInvoice;
    procedure EditInvoice;
  public
    { Public declarations }
    InvoiceOperation: TOperation;
    InvoiceID: integer;
    Bookmark: TBookmark;
    Status: integer;
    Modified: boolean;
    DetailsID: array of integer;
  end;

var
  fmInvoice: TfmInvoice;

implementation

{$R *.dfm}

procedure TfmInvoice.btnAddDetailsClick(Sender: TObject);
var
  LastRow: integer;
  i: integer;
  n: Currency;
begin
  LastRow := 0;
  if frmRTFEditor2.Editor.Text <> '' then begin
    case Operation of
      opNew: begin
        if (sgDetails.Cells[1, sgDetails.RowCount - 1] <> '') and
           (sgDetails.Cells[2, sgDetails.RowCount - 1] <> '') then
          sgDetails.RowCount := sgDetails.RowCount + 1;
        LastRow := sgDetails.RowCount - 1;
      end;
      opEdit: begin
        LastRow := sgDetails.Row;
        Operation := opNew;
        btnAddDetails.Caption := '&Add';
      end;
    end;
    sgDetails.Cells[0, LastRow] := IntToStr(LastRow);
    sgDetails.Cells[1, LastRow] := frmRTFEditor2.Editor.Text;
    sgDetails.Objects[1, LastRow] := TMemoryStream.Create;
    frmRTFEditor2.Editor.Lines.SaveToStream(TMemoryStream(sgDetails.Objects[1, LastRow]));
    sgDetails.Cells[2, LastRow] := txtAmount.Text;
    sgDetails.Cells[3, LastRow] := BoolToStr(cbFOCDI.Checked, True);
    frmRTFEditor2.Editor.Text := '';
    txtAmount.Text := '0.00';
    cbFOCDI.Checked := False;
    Modified := True;
    n := 0;
    for i := 1 to sgDetails.RowCount - 1 do
      n := n + StrToFloat(sgDetails.Cells[2, i]);
    lbTotalAmount.Caption := Format('%.2f', [n]);
  end else
    ShowMessage('Description can''t be empty.');
end;

procedure TfmInvoice.btnDeleteDescriptionClick(Sender: TObject);
var
  CurrRow, NextRow: integer;
begin
  lbTotalAmount.Caption := Format('%.2f', [StrToFloat(lbTotalAmount.Caption) - StrToFloat(sgDetails.Cells[2, sgDetails.Row])]);
  CurrRow := sgDetails.Row;
  TMemoryStream(sgDetails.Objects[1, CurrRow]).Free;
  sgDetails.Objects[1, CurrRow] := nil;
  Modified := True;
  if sgDetails.RowCount > 2 then begin
    repeat
      NextRow := CurrRow + 1;
      sgDetails.Cells[1, CurrRow] := sgDetails.Cells[1, NextRow];
      sgDetails.Objects[1, CurrRow] := sgDetails.Objects[1, NextRow];
      sgDetails.Cells[2, CurrRow] := sgDetails.Cells[2, NextRow];
      sgDetails.Cells[3, CurrRow] := sgDetails.Cells[3, NextRow];
      CurrRow := NextRow;
    until CurrRow >= sgDetails.RowCount;
    sgDetails.RowCount := sgDetails.RowCount - 1;
  end else begin
    sgDetails.Cells[1, 1] := '';
    sgDetails.Cells[2, 1] := '';
    sgDetails.Cells[3, 1] := '';
    Operation := opNew;
    btnAddDetails.Caption := '&Add';
  end;
  with DataModule1.qrySQL do
  begin
    SQL.Clear;
    Params.Clear;
    try
      SQL.Add('delete from Invoice_Details where ID=:ID');
      Params.ParamByName('ID').AsInteger := DetailsID[CurrRow - 2];
      if not Prepared then
        Prepare;
      ExecSQL;
    finally
      Transaction.Commit;
    end;
  end;
end;

procedure TfmInvoice.btnSaveClick(Sender: TObject);
begin
  case InvoiceOperation of
    opNew:  AddNewInvoice;
    opEdit: EditInvoice;
  end;
  Modified := False;
end;

procedure TfmInvoice.cbCompanyNameChange(Sender: TObject);
begin
  Modified := True;
end;

procedure TfmInvoice.cbFOCDIClick(Sender: TObject);
begin
  Modified := True;
end;

procedure TfmInvoice.dtDateOfShootExit(Sender: TObject);
begin
  dtDueDate.Date := IncDay(dtDateOfShoot.Date,
                           DataModule1.fbConfig.FieldByName('INVOICE_PAY_TERM').AsInteger);
end;

procedure TfmInvoice.dbcbCompanyNameCloseUp(Sender: TObject);
begin
  with DataModule1.fbProducts do
  begin
    if not Active then
      Open;
    Last;
    First;
  end;
end;

procedure TfmInvoice.InitHeader;
begin
  with DataModule1.fbConfig do
  begin
    if not Active then
      Open;
    LastInvoiceNo := FieldByName('LAST_INVOICE_NO').AsInteger;
    lblInvoiceNo.Caption := GenerateInvoiceNo(FieldByName('INVOICE_PREFIX').AsString,
                                FieldByName('INVOICE_NO_LENGTH').AsInteger,
                                LastInvoiceNo);
  end;
  Inc(LastInvoiceNo);
  dbcbCompanyName.KeyValue := '';
  frmRTFEditor1.Editor.Clear;
  txtAmount.Text := '0.00';
  lbTotalAmount.Caption := '0.00';
  btnAddDetails.Caption := '&Add';
end;

procedure TfmInvoice.AddNewInvoice;
var
  RTFStream: TMemoryStream;
  RTFString: AnsiString;
  i: Integer;
  P1, P2: integer;
begin
  if dbcbCompanyName.Text <> '' then
  begin
    RTFStream := TMemoryStream.Create;
    RTFStream.Clear;
    try
      with DataModule1.qrySQL do
      begin
        SQL.Clear;
        Params.Clear;
        try
          SQL.Add('insert into Invoice (INVOICE_NO, INVOICE_DATE, INVOICE_DUE_DATE, ' +
                  'DATE_OF_SHOOT, CUSTOMER_ID, "FOR", FOR_RTF, TOTAL_AMOUNT, ' +
                  'FOR_PRODUCT, PUBLISH_ON, PERSON_IN_CHARGE, JOBSHEET_NO, ' +
                  'PHOTOGRAPHER1_ID, PHOTOGRAPHER2_ID, CREATE_DATE, ' +
                  'UPDATE_DATE) values (:InvoiceNo, :InvoideDate, :DueDate, ' +
                  ':DateOfShoot, :CustomerID, :For, :For_RTF, :TotalAmount, ' +
                  ':ProductName, :PublishOn, :PersonInCharge, :JobSheetNo, ' +
                  ':Photographer1ID, :Photographer2ID, CURRENT_TIMESTAMP, ' +
                  'CURRENT_TIMESTAMP)');
          frmRTFEditor1.Editor.Lines.SaveToStream(RTFStream);
          RTFStream.Position := 0;
          //Read from the stream into an AnsiString (rtfString)
          if (RTFStream.Size > 0) then
          begin
            SetLength(RTFString, RTFStream.Size);
            if (RTFStream.Read(RTFString[1], RTFStream.Size) <= 0) then
              raise EStreamError.CreateFmt('End of stream reached with %d bytes left to read.', [RTFStream.Size]);
          end;
          Params.ParamByName('InvoiceNo').AsString := lblInvoiceNo.Caption;
          Params.ParamByName('InvoideDate').AsDate := Date;
          Params.ParamByName('DueDate').AsDate := dtDueDate.Date;
          Params.ParamByName('DateOfShoot').AsDate := dtDateOfShoot.Date;
          Params.ParamByName('CustomerID').AsInteger := dbcbCompanyName.ListSource.DataSet.FieldByName('ID').AsInteger;
          Params.ParamByName('For').AsString := frmRTFEditor1.Editor.Text;
          Params.ParamByName('For_RTF').AsAnsiString := RTFString;
          Params.ParamByName('TotalAmount').AsString := lbTotalAmount.Caption;
          Params.ParamByName('ProductName').AsString := VarToStr(dbcbProduct.KeyValue);
          Params.ParamByName('PublishOn').AsString := txtPublishOn.Text;
          Params.ParamByName('PersonInCharge').AsString := txtPersonInCharge.Text;
          Params.ParamByName('JobSheetNo').AsString := txtJobsheetNo.Text;
          if not TryStrToInt(VarToStr(dbcbPhotographer1.KeyValue), P1) then
            P1 := 0;
          if not TryStrToInt(VarToStr(dbcbPhotographer2.KeyValue), P2) then
            P2 := 0;
          Params.ParamByName('Photographer1ID').AsInteger := P1;
          Params.ParamByName('Photographer2ID').AsInteger := P2;
          if not Prepared then
            Prepare;
          ExecSQL;
          Transaction.Commit;
        except
          on E: EIBInterBaseError do
          begin
            Transaction.Rollback;
            ShowMessage(Format('IBErrorCode=%d, SQLCode=%d, Msg=%s, StackTrace=%s', [E.IBErrorCode, E.SQLCode, E.Message, E.StackTrace]));
          end;
        end;

        SQL.Clear;
        Params.Clear;
        SQL.Add('insert into Invoice_Details (INVOICE_NO, DESCRIPTION, ' +
                'DESCRIPTION_RTF, AMOUNT, FOC_DI, CREATE_DATE, ' +
                'UPDATE_DATE) values (:InvoiceNo, :Description, :Description_RTF,' +
                ':Amount, :FOC, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)');
        for i := 1 to sgDetails.RowCount - 1 do
        begin
          RTFStream.Clear;
          RTFString := '';
          try
            TMemoryStream(sgDetails.Objects[1, i]).SaveToStream(RTFStream);
            RTFStream.Position := 0;
            //Read from the stream into an AnsiString (rtfString)
            if (RTFStream.Size > 0) then
            begin
              SetLength(RTFString, RTFStream.Size);
              if (RTFStream.Read(RTFString[1], RTFStream.Size) <= 0) then
                raise EStreamError.CreateFmt('End of stream reached with %d bytes left to read.', [RTFStream.Size]);
            end;
            Params.ParamByName('InvoiceNo').AsString := lblInvoiceNo.Caption;
            Params.ParamByName('Description').AsString := sgDetails.Cells[1, i];
            Params.ParamByName('Description_RTF').AsAnsiString := RTFString;
            Params.ParamByName('Amount').AsString := Trim(sgDetails.Cells[2, i]);
            Params.ParamByName('FOC').AsString := Trim(sgDetails.Cells[3, i]);
            if not Prepared then
              Prepare;
            ExecSQL;
            Transaction.Commit;
          except
            on E: EIBInterBaseError do
            begin
              Transaction.Rollback;
              ShowMessage(Format('IBErrorCode=%d, SQLCode=%d, Msg=%s, StackTrace=%s', [E.IBErrorCode, E.SQLCode, E.Message, E.StackTrace]));
            end;
          end;
        end;
      end;
    finally
      RTFStream.Free;
    end;

    with DataModule1.qryConfig do
    begin
      SQL.Clear;
      Params.Clear;
      try
        try
          SQL.Add('update Config set Last_Invoice_No=:LastInvoiceNo, Update_Date=CURRENT_TIMESTAMP');
          Params.ParamByName('LastInvoiceNo').AsInteger := LastInvoiceNo;
          if not Prepared then
            Prepare;
          ExecSQL;
          Transaction.Commit;
        except
          on E: EIBInterBaseError do
          begin
            Transaction.Rollback;
            ShowMessage(Format('IBErrorCode=%d, SQLCode=%d, Msg=%s, StackTrace=%s', [E.IBErrorCode, E.SQLCode, E.Message, E.StackTrace]));
          end;
        end;
      finally

      end;
    end;

    if not DataModule1.qryInvoiceList.Active then
      DataModule1.qryInvoiceList.Active := True;
    if not DataModule1.fbInvoice.Active then
      DataModule1.fbInvoice.Active := True;
    if not DataModule1.fbInvoiceDetails.Active then
      DataModule1.fbInvoiceDetails.Active := True;

    InitHeader;
    for i := 1 to sgDetails.RowCount - 1 do
      if sgDetails.Objects[1, i] <> nil then
        TMemoryStream(sgDetails.Objects[1, i]).Free;
    sgDetails.RowCount := 2;
    sgDetails.Cells[1, 1] := '';
    sgDetails.Cells[2, 1] := '';
    sgDetails.Cells[3, 1] := '';
    btnAddDetails.Caption := '&Add';
    cbFOCDI.Checked := False;
  end
  else
    ShowMessage('Company Name is required.');
end;

procedure TfmInvoice.EditInvoice;
var
  RTFStream: TMemoryStream;
  RTFString: AnsiString;
  i: Integer;
  P1, P2: integer;
begin
  if dbcbCompanyName.Text <> '' then
  begin
    RTFStream := TMemoryStream.Create;
    RTFStream.Clear;
    frmRTFEditor1.Editor.Lines.SaveToStream(RTFStream);
    RTFStream.Position := 0;
    //Read from the stream into an AnsiString (rtfString)
    if (RTFStream.Size > 0) then
    begin
      SetLength(RTFString, RTFStream.Size);
      if (RTFStream.Read(RTFString[1], RTFStream.Size) <= 0) then
        raise EStreamError.CreateFmt('End of stream reached with %d bytes left to read.', [RTFStream.Size]);
    end;
    with DataModule1.qrySQL do
    begin
      SQL.Clear;
      Params.Clear;
      try
        SQL.Text := 'update Invoice set "FOR"=:For, FOR_RTF=:For_RTF, ' +
                    'TOTAL_AMOUNT=:TotalAmount, DATE_OF_SHOOT=:DateOfShoot, ' +
                    'INVOICE_DUE_DATE=:DueDate, FOR_PRODUCT=:ProductName, ' +
                    'PUBLISH_ON=:PublishOn, PERSON_IN_CHARGE=:PersonInCharge, ' +
                    'JOBSHEET_NO=:JobSheetNo, PHOTOGRAPHER1_ID=:Photographer1ID, ' +
                    'PHOTOGRAPHER2_ID=:Photographer2ID, UPDATE_DATE=CURRENT_TIMESTAMP ' +
                    'where INVOICE_NO=:InvoiceNo';
        Params.ParamByName('For').AsString := frmRTFEditor1.Editor.Text;
        Params.ParamByName('For_RTF').AsAnsiString := RTFString;
        Params.ParamByName('TotalAmount').AsString := lbTotalAmount.Caption;
        Params.ParamByName('DateOfShoot').AsDate := dtDateOfShoot.Date;
        Params.ParamByName('DueDate').AsDate := dtDueDate.Date;
        Params.ParamByName('InvoiceNo').AsString := lblInvoiceNo.Caption;
        Params.ParamByName('ProductName').AsString := VarToStr(dbcbProduct.KeyValue);
        Params.ParamByName('PublishOn').AsString := txtPublishOn.Text;
        Params.ParamByName('PersonInCharge').AsString := txtPersonInCharge.Text;
        Params.ParamByName('JobSheetNo').AsString := txtJobsheetNo.Text;
        if not TryStrToInt(VarToStr(dbcbPhotographer1.KeyValue), P1) then
          P1 := 0;
        if not TryStrToInt(VarToStr(dbcbPhotographer2.KeyValue), P2) then
          P2 := 0;
        Params.ParamByName('Photographer1ID').AsInteger := P1;
        Params.ParamByName('Photographer2ID').AsInteger := P2;
        if not Prepared then
          Prepare;
        ExecSQL;
        Transaction.Commit;
      except
        on E: EIBInterBaseError do
        begin
          Transaction.Rollback;
          ShowMessage(Format('IBErrorCode=%d, SQLCode=%d, Msg=%s, StackTrace=%s', [E.IBErrorCode, E.SQLCode, E.Message, E.StackTrace]));
        end;
      end;
    end;

    with DataModule1.qrySQL do
    begin
      try
        try
          for i := 1 to sgDetails.RowCount - 1 do
          begin
            SQL.Clear;
            Params.Clear;
            RTFStream.Clear;
            RTFString := '';
            TMemoryStream(sgDetails.Objects[1, i]).SaveToStream(RTFStream);
            RTFStream.Position := 0;
            //Read from the stream into an AnsiString (rtfString)
            if (RTFStream.Size > 0) then
            begin
              SetLength(RTFString, RTFStream.Size);
              if (RTFStream.Read(RTFString[1], RTFStream.Size) <= 0) then
                raise EStreamError.CreateFmt('End of stream reached with %d bytes left to read.', [RTFStream.Size]);
            end;
            //check whether added more items then before
            if i <= Length(DetailsID) then
            begin
              SQL.Add('update Invoice_Details set DESCRIPTION=:Description, ' +
                      'DESCRIPTION_RTF=:Description_RTF, AMOUNT=:Amount, ' +
                      'FOC_DI=:FOC, UPDATE_DATE=CURRENT_TIMESTAMP where ID=:ID');
              Params.ParamByName('ID').AsInteger := DetailsID[i - 1];
            end else begin
              SQL.Add('insert into Invoice_Details (INVOICE_NO, DESCRIPTION, ' +
                      'DESCRIPTION_RTF, AMOUNT, FOC_DI, CREATE_DATE, UPDATE_DATE) ' +
                      'values (:InvoiceNo, :Description, :Description_RTF,' +
                      ':Amount, :FOC, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)');
              Params.ParamByName('InvoiceNo').AsString := lblInvoiceNo.Caption;
            end;
            Params.ParamByName('Description').AsString := sgDetails.Cells[1, i];
            Params.ParamByName('Description_RTF').AsAnsiString := RTFString;
            Params.ParamByName('Amount').AsString := Trim(sgDetails.Cells[2, i]);
            Params.ParamByName('FOC').AsString := Trim(sgDetails.Cells[3, i]);

            if not Prepared then
              Prepare;
            ExecSQL;
          end;
          Transaction.Commit;
        except
          on E: EIBInterBaseError do
          begin
            Transaction.Rollback;
            ShowMessage(Format('IBErrorCode=%d, SQLCode=%d, Msg=%s, StackTrace=%s', [E.IBErrorCode, E.SQLCode, E.Message, E.StackTrace]));
          end;
        end;
      finally
        RTFStream.Free;
      end;
    end;

    if not DataModule1.qryInvoiceList.Active then
      DataModule1.qryInvoiceList.Active := True;
    if not DataModule1.fbInvoice.Active then
      DataModule1.fbInvoice.Active := True;
    if not DataModule1.fbInvoiceDetails.Active then
      DataModule1.fbInvoiceDetails.Active := True;

    Close;
  end else
    ShowMessage('Company Name is required.');
end;

procedure TfmInvoice.btnCloseClick(Sender: TObject);
begin
  if Modified then begin
    case MessageDlg('Invoice has updated, save and exit?', mtWarning, mbYesNoCancel, 0, mbYes) of
      mrYes: begin
        btnSave.Click;
        Close;
      end;
      mrNo: begin
        Close;
      end;
    end;
  end else
    Close;
end;

procedure TfmInvoice.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  DataModule1.qryInvoiceList.Locate('ID', InvoiceID, []);
  FreeAndNil(fmInvoice);
end;

procedure TfmInvoice.FormCreate(Sender: TObject);
begin
  InvoiceOperation := opNew;
  Modified := False;
  lbDate.Caption := FormatDateTime('dd mmm yyyy', Date);
  sgDetails.ColWidths[0] := 30;
  sgDetails.ColWidths[1] := 300;
  sgDetails.ColWidths[2] := 100;
  sgDetails.Cells[0, 0] := 'No.';
  sgDetails.Cells[1, 0] := 'Description';
  sgDetails.Cells[2, 0] := 'Amount';
  sgDetails.Cells[3, 0] := 'FOC DI';
  sgDetails.Cells[0, 1] := '1';
  with DataModule1.fbCustomers do
  begin
    if not Active then
      Open;
    Last;
    First;
  end;
  Operation := opNew;
  dtDateOfShoot.Date := Date;
  dtDueDate.Date := Date;
  InitHeader;
  frmRTFEditor1.Init;
  frmRTFEditor2.Init;
end;

procedure TfmInvoice.frmRTFEditor1EditorChange(Sender: TObject);
begin
  Modified := True;
end;

procedure TfmInvoice.frmRTFEditor2EditorChange(Sender: TObject);
begin
  Modified := True;
end;

procedure TfmInvoice.sgDetailsDblClick(Sender: TObject);
begin
  if sgDetails.Row > 0 then
  begin
    frmRTFEditor2.Editor.Text := sgDetails.Cells[1, sgDetails.Row];
    if sgDetails.Objects[1, sgDetails.Row] <> nil then
    begin
      TMemoryStream(sgDetails.Objects[1, sgDetails.Row]).Seek(0, soFromBeginning);
      frmRTFEditor2.Editor.Lines.LoadFromStream(TMemoryStream(sgDetails.Objects[1, sgDetails.Row]));
    end;
    txtAmount.Text := sgDetails.Cells[2, sgDetails.Row];
    cbFOCDI.Checked := StrToBool(sgDetails.Cells[3, sgDetails.Row]);
  end;
  Operation := opEdit;
  btnAddDetails.Caption := 'S&ave';
  frmRTFEditor2.Editor.SelectAll;
  frmRTFEditor2.Editor.SetFocus;
end;

procedure TfmInvoice.txtAmountChange(Sender: TObject);
begin
  Modified := True;
end;

procedure TfmInvoice.txtAmountExit(Sender: TObject);
begin
  txtAmount.Text := Format('%.2f', [StrToFloat(txtAmount.Text)]);
end;

procedure TfmInvoice.txtAmountKeyPress(Sender: TObject; var Key: Char);
var
  i: integer;
begin
  case Key of
    '0'..'9':;
    '.': begin
           i := Pos('.', txtAmount.Text);
           if i > 0 then
           begin
             Key := #0;
             txtAmount.SelStart := i + 1;
             txtAmount.SelLength := Length(txtAmount.Text) - i;
           end;
         end;
    Char(VK_BACK):;
    else
      Key := #0;
  end;
end;

end.
